<?php

// Copyright 2019-2022 Hackware SpA <human@hackware.cl>
// This file is part of "Hackware Web Services Payment" and licensed under
// the terms of the GNU Affero General Public License version 3, or (at your
// option) a later version. You should have received a copy of this license
// along with the software. If not, see <https://www.gnu.org/licenses/>.

namespace Hawese\Payment\Gateways;

use Hawese\Core\Support\Arr;
use Hawese\Payment\Support\Http as HttpSupport;
use Nyholm\Psr7\Factory\Psr17Factory; // Request, Stream & Uri Factory
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\StreamInterface;
use LogicException;

abstract class AbstractRequestBuilder
{
    abstract protected function getEndpoint(): string;

    protected $psr17Factory;

    protected $method;
    protected $uri;
    protected $bodyParams;
    protected $headers;

    /**
     * @param string $method HTTP method.
     * @param string $uri URI to remote resource, it may be relative
     * (begins with /) or absolute.
     * @param mixed $body associative array with body (as in forms) params.
     * @param array $headers associative array with headers.
     */
    public function __construct(
        string $method,
        string $uri,
        $bodyParams = null,
        array $headers = []
    ) {
        $this->psr17Factory = new Psr17Factory();

        $this->method = $method;
        $this->uri = $uri;
        $this->bodyParams = $bodyParams;
        $this->headers = $headers;
        $this->mergeDefaultHeaders();
    }

    protected function mergeDefaultHeaders()
    {
        if (
            $this->method == 'POST' &&
            Arr::keyExists($this->headers, 'Content-Type')
        ) {
            $this->headers = array_merge($this->headers, [
                'Content-Type' => 'application/x-www-form-urlencoded',
            ]);
        }
    }

    /**
     * @returns Psr7 Request
     */
    public function build(): RequestInterface
    {
        $request = $this->psr17Factory
            ->createRequest($this->method, $this->buildUri())
            ->withBody($this->buildBody());

        $request = $this->addHeaders($request);

        return $request;
    }

    protected function addHeaders(RequestInterface $request): RequestInterface
    {
        foreach ($this->headers as $key => $value) {
            $request = $request->withHeader($key, $value);
        }
        return $request;
    }

    protected function buildUri()
    {
        if ($this->uri[0] == '/') {
            $this->uri = $this->getEndpoint() . $this->uri;
        }

        return $this->psr17Factory->createUri($this->uri);
    }

    protected function buildBody(): StreamInterface
    {
        if (Arr::get($this->headers, 'Content-Type') === 'application/json') {
            $body = json_encode($this->bodyParams);
        } else {
            $body = HttpSupport::buildFormQuery($this->bodyParams ?? []);
        }
        return $this->psr17Factory->createStream($body);
    }

    public function getQueryParams(): array
    {
        parse_str($this->buildUri()->getQuery(), $queryParams);
        return $queryParams;
    }

    /**
     * @returns array of body or query params, fails if there are both.
     */
    protected function getBodyOrQueryParams(): array
    {
        if ($this->bodyParams) {
            if (strstr($this->uri, '?') !== false) {
                throw new LogicException(
                    "There are body and query params, I can't handle this >.<",
                    2999
                );
            }
            return $this->bodyParams;
        }
        return $this->getQueryParams();
    }
}
